package framework.model {
	import Box2D.Collision.Shapes.b2CircleShape;
	import Box2D.Collision.Shapes.b2PolygonShape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2FixtureDef;
	import Box2D.Dynamics.b2World;
	import framework.signals.WorldUpdatedSignal;
	import org.robotlegs.mvcs.Actor;

	/**
	 * @author Charlie MacIsaac
	 */
	public class Box2DWorldModel extends Actor {
		// PROPERTIES =============================================================================
		[Inject]	public var worldUpdated : WorldUpdatedSignal;
		
		private var _world : b2World;
		private var _gravity : b2Vec2;
		private var _bodies : Vector.<b2Body>;
		
		private var _timeStep : Number;
		private var _velocityIterations : uint;
		private var _positionIterations : uint;
		
		private var _maxWidth : Number;
		private var _maxHeight : Number;
		
		private var _circle : b2Body;
		private var _movement : Number;
		
		// GETTERS + SETTERS ======================================================================
		public function moveCircle(movement : Number) : void {
			_movement = movement;
		}
		
		// CONSTRUCTOR ============================================================================
		public function Box2DWorldModel() {
			_timeStep = 1 / 20;
			_velocityIterations = 8;
			_positionIterations = 4;
			
			_bodies = new Vector.<b2Body>();
			_gravity = new b2Vec2(0, 20);
			_world = new b2World(_gravity, true);
		}
		
		// PUBLIC =================================================================================
		public function init(groundThickness : Number, width : Number, height : Number) : void {
			_maxHeight = height;
			_maxWidth = width;
			createGround(0, height - groundThickness, width, groundThickness);
		}
		
		public function refresh() : void {
			if (_circle &&  _movement != 0) {
				var absoluteLinearVelocityX : Number = Math.abs(_circle.GetLinearVelocity().x);
				
				//if (absoluteLinearVelocityX < 30 * _movement) {
					var moveVector : b2Vec2 = new b2Vec2(5000 * _movement, 0);
					_circle.ApplyImpulse(moveVector, _circle.GetWorldCenter());
				//}
			}
			
			_world.Step(_timeStep, _velocityIterations, _positionIterations);
			_world.Step(_timeStep, _velocityIterations, _positionIterations);
			_world.ClearForces();
			
			worldUpdated.dispatch(_bodies);
		}
		
		public function createCircle(radius : Number) : uint {
			var circleBodyDef : b2BodyDef = new b2BodyDef();
			circleBodyDef.type = b2Body.b2_dynamicBody;
			circleBodyDef.position.Set(radius, _maxHeight - radius * 2 - 20);
			
			var circleShape : b2CircleShape = new b2CircleShape(radius);
			circleShape.SetLocalPosition(new b2Vec2(radius, radius));
			
			var circleFixtureDef : b2FixtureDef = new b2FixtureDef();
			circleFixtureDef.density = 1;
			circleFixtureDef.friction = 0.3;
			circleFixtureDef.restitution = 0.2;
			circleFixtureDef.shape = circleShape;
			
			_circle = _world.CreateBody(circleBodyDef);
			_circle.CreateFixture(circleFixtureDef);
			
			_movement = 0;
			
			return _bodies.push(_circle) - 1;
		}
		
		public function createBody(width : Number, height : Number) : uint {
			var randomX : Number = width + Math.random() * (_maxWidth - width);
			var randomY : Number = Math.random() * -200;
			var randomAngle : Number = Math.random() * 2 * Math.PI;
			
			var bodyDef : b2BodyDef = new b2BodyDef();
			bodyDef.type = b2Body.b2_dynamicBody;
			bodyDef.position.Set(randomX, randomY);
			bodyDef.angle = randomAngle;
			
			var bodyVectors : Vector.<b2Vec2> = new Vector.<b2Vec2>();
			bodyVectors.push(new b2Vec2(0, 0));
			bodyVectors.push(new b2Vec2(width, 0));
			bodyVectors.push(new b2Vec2(width, height));
			bodyVectors.push(new b2Vec2(0, height));
			
			var bodyShape : b2PolygonShape = new b2PolygonShape();
			bodyShape.SetAsVector(bodyVectors, bodyVectors.length);
			
			var bodyFixtureDef : b2FixtureDef = new b2FixtureDef();
			bodyFixtureDef.density = 0.5;
			bodyFixtureDef.friction = 0.3;
			bodyFixtureDef.restitution = 0.2;
			bodyFixtureDef.shape = bodyShape;
			
			var body : b2Body = _world.CreateBody(bodyDef);
			body.CreateFixture(bodyFixtureDef);
			
			return _bodies.push(body) - 1;
		}
		
		// PRIVATE ================================================================================
		private function createGround(x : Number, y : Number, width : Number, height: Number) : void {
			var groundBodyDef : b2BodyDef = new b2BodyDef();
			groundBodyDef.position.Set(x, y);
			
			var groundVectors : Vector.<b2Vec2> = new Vector.<b2Vec2>();
			groundVectors.push(new b2Vec2(0, 0));
			groundVectors.push(new b2Vec2(width, 0));
			groundVectors.push(new b2Vec2(width, height));
			groundVectors.push(new b2Vec2(0, height));
			
			var groundShapeDef : b2PolygonShape = new b2PolygonShape();
			groundShapeDef.SetAsVector(groundVectors, groundVectors.length);
			
			var groundFixtureDef : b2FixtureDef = new b2FixtureDef();
			groundFixtureDef.shape = groundShapeDef;
			groundFixtureDef.density = 1;
			groundFixtureDef.friction = 1;
			
			var groundBody : b2Body = _world.CreateBody(groundBodyDef);
			groundBody.CreateFixture(groundFixtureDef);
			
			_bodies.push(groundBody);
		}
		
		// LISTENERS ==============================================================================
		
	}
}